package pfctrller;

import java.util.LinkedList;
import java.util.List;

/**
 * The common feature of a a device:
 * - has a dpid
 * - a device have Ports
 * - different devices are connected by Pipes
 * */
public abstract class Device {
	protected long DPID;
	protected int numOfPort;
	protected Port[] ports = null;
	protected List<Pipe> pipes = null;
	protected long transmissionRate;// in Byte
	
	/**
	 * Constructor
	 * @param dPID
	 * @param numOfPort
	 */
	public Device(long dPID, int numOfPort, long rate) {
		super();
		DPID = dPID;
		this.numOfPort = numOfPort;
		this.transmissionRate = rate;
		
		init();
	}
	
	private void init(){
		ports = new Port[numOfPort];
		for(int i = 0; i < numOfPort; i++){
			ports[i] = new Port(DPID, i);
		}
		pipes = new LinkedList<Pipe>();
	}
	
	/**
	 * Connect to another device
	 * @param rhs the other Device to connect
	 * @param ports the port numbers used to connect with this Device
	 * */
	public void connectTo(Device rhs, int[] from, int[] to) throws Exception {
		if(from.length != to.length){
			throw new Exception("Port number not match");
		}
		Port[] srcs = new Port[from.length];
		Port[] dsts = new Port[from.length];
		for(int i = 0; i < from.length; i++){
			srcs[i] = ports[from[i]];
			dsts[i] = rhs.getPort(to[i]);
		}
		Pipe p = new Pipe(this, rhs, srcs, dsts);
		pipes.add(p);
	}
	
	// Getter and Setter
	
	/**
	 * @return the transmissionRate
	 */
	public long getTransmissionRate() {
		return transmissionRate;
	}

	/**
	 * @param transmissionRate the transmissionRate to set
	 */
	public void setTransmissionRate(long transmissionRate) {
		this.transmissionRate = transmissionRate;
	}

	public Port getPort(int portNum) throws IndexOutOfBoundsException{
		return ports[portNum];
	}
	
	/**
	 * Get the Pipe connected to
	 * @param dstDevice
	 * @return null if they are not connected
	 * */
	public Pipe getPipe(Device dstDevice){
		for (Pipe p : this.pipes) {
			if(p.getDst().DPID == dstDevice.DPID){
				return p;
			}
		}
		return null;
	}
	
	/**
	 * Accommodate a flow.
	 * */
	public void accommodate(Flow f){
		//TODO
		// If the flow already exists, throttle it
		// else, add a new flow and allocation bw for it
	}
}
